function showPopup(Id){
    // 获取弹窗元素
    var popup = document.getElementById(Id);
    var close_button = popup.querySelector('.rei-close-window-button')
    close_button.onclick = function () {
        popup.style.visibility = 'hidden';
    };
    // 获取窗口宽度和高度
    var windowWidth = window.innerWidth;
    var windowHeight = window.innerHeight;
    // 获取弹窗自身宽度和高度
    var popupWidth = popup.offsetWidth;
    var popupHeight = popup.offsetHeight;

    // 计算弹窗在水平和垂直方向上的居中位置
    var x = (windowWidth - popupWidth) / 2;
    var y = (windowHeight - popupHeight) / 2;

    // 设置弹窗的位置
    popup.style.left = x + 'px';
    popup.style.top = y + 'px';

    // 显示弹窗
    popup.style.visibility = 'visible';
}

function addDropDown(id,options) {
    const DropDown = document.getElementById(id);
    DropDown.classList.add('DropDown');
    const DropDown_list = document.createElement('div')
    DropDown_list.classList.add('DropDown-list')
    options = options || {};
    var text_len=4;
    for(const key in options){
        var new_span = document.createElement('span');
        new_span.id = key;
        if (text_len < options[key].length){
            text_len = options[key].length
        }
        new_span.textContent = options[key];
        new_span.classList.add('DropDown-button');
        DropDown_list.appendChild(new_span)
    }
    DropDown_list.style.width = (text_len*16+10) + 'px';
    DropDown.appendChild(DropDown_list)
    DropDown.style.cursor = 'pointer';
}

function addTips(id,tip_txt){
    const tip = document.getElementById(id)
    const tipTxt = document.createElement('span')
    tip.classList.add('tips');
    tipTxt.classList.add('tips-txt');
    tipTxt.style.width = (tip_txt.length*12) + 'px';
    tipTxt.textContent = tip_txt
    tip.appendChild(tipTxt)
}


var rei_ros = {}

let ros_is_linked = false

// 最大重连次数
const MAX_RECONNECTIONS = 5;

// 重连间隔时间（毫秒）
const RECONNECT_INTERVAL = 3000;

// 当前重连次数
let reconnectCount = 0;

rei_ros.linkMaster = function (ROS_IP = '192.168.8.1',lamp_div) {
    lamp = document.getElementById(lamp_div)
    try{
        var ros = new ROSLIB.Ros({
            url : 'ws://'+ROS_IP+':9090'
        });
        // ros_is_linked = false
        // 检查连接状态
        ros.on('connection', function () {
            console.log('Connected to ROS');
            reconnectCount = 0;
            ros_is_linked = true;
            lamp.style.backgroundColor = "#0aff02";
        });
    
        ros.on('error', function (error) {
            console.log('Error connecting to ROS: ' + error);
            ros_is_linked = false;
            lamp.style.backgroundColor = "#ff0202";
        });
    
        ros.on('close', function () {
            console.log('Connection to ROS closed');
            ros_is_linked = false;
            lamp.style.backgroundColor = "#ff0202";
        });
        return ros
    } catch (e) {
        console.log(e)
    }
}

var selected_robot = ''


class rei_robot_slam_core{
    constructor(options) {
        options = options || {};
        var robotName = options.robotName;
    }
}


class rei_robot_core{
    constructor(options) {
        options = options || {};
        var robotName = options.robotName;
        this.container = document.getElementById('robot-tag');
        this.robotController = document.getElementById('robot_controller')
        selected_robot = 'robot-tag-button'
        this.addRoboCard({
            CardID:'base-tag-button',
            ImgSource:'static/img/'+robotName+'.png',
            CardName: robotName,
            CardTool:'base-tool',
            is_start:true
        })
        //更新card选中状态
        this.container.onclick = function () {
            var allCard = this.querySelectorAll('.App-blockly-robot-card')
            allCard.forEach(card => {
                if(card.id == selected_robot){
                    card.style.border = '2px solid blue';
                }else{
                    card.style.border = ''
                }  
            })
        }
    }

    addRobotTool(options){
        
    }   

    addRoboCard(options){
        options = options || {};
        var cardID = options.CardID;
        var imgSource = options.ImgSource;
        var cardName = options.CardName;
        var CardTool = options.CardTool;
        var is_start = options.is_start || false;
        var selftool = document.getElementById(CardTool);
        const card = document.createElement('div');
        card.id = cardID;
        card.classList.add('App-blockly-robot-card');
        const img = document.createElement('img');
        img.src = imgSource;
        const captionElement = document.createElement('p');
        captionElement.textContent = cardName;
        card.appendChild(img);
        card.appendChild(captionElement);
        this.container.appendChild(card);
        if(is_start==true){
            card.style.border = '2px solid blue'
            selftool.style.display = 'block'
        }
        //设备卡片选择显示对应的工具卡片
        card.onclick = function () {
            var robot_tool = document.querySelectorAll('.App-blockly-robot-tool');
            robot_tool.forEach(tool => {
                tool.style.display = 'none';
            })
            selected_robot = cardID;
            selftool.style.display = 'block'
        }
        //根据类别识别，添加工具卡片导航栏
        const pages = selftool.querySelectorAll('.App-blockly-robot-tool-tag-page')
        const nav = selftool.querySelector('.App-blockly-robot-tool-nav')
        var selected_tag = pages[0]
        pages.forEach(page=>{
            var nav_tag = document.createElement('span')
            nav_tag.id = page.id + 'page'
            nav_tag.classList.add('App-blockly-robot-tool-tag')
            var tagNode = document.createTextNode(page.title)
            nav_tag.appendChild(tagNode)
            nav_tag.onclick = function () {
                selected_tag = page
            }
            nav.appendChild(nav_tag)
            if(page.id==selected_tag.id){
                page.style.display = 'block'
                nav_tag.style.backgroundColor = '#FFFFFF'
            }
        })
        nav.onclick=function (){
            pages.forEach(page=>{
                page.style.display = 'none'
            })
            var tags = this.querySelectorAll('.App-blockly-robot-tool-tag')
            tags.forEach(tag=>{
                if(selected_tag.id+'page'==tag.id){
                    tag.style.background = '#FFFFFF'
                }else{
                    tag.style.background = '#d0d0d0'
                } 
            })
            selected_tag.style.display = 'block'
        }
        
    }
}

class rei_viewer{
    constructor(options) {
        options = options || {};
        this.ros = options.ros;
        this.scan_topic = options.scan_topic;
        this.cameraPose = options.cameraPose || {
            x: 0,
            y: 0,
            z: 5,
        }
        var divID = options.divID;
    
        this.div = document.getElementById(divID)
        this.loading = document.createElement('div')
        this.loading
        var width = this.div.clientWidth;
        var height = this.div.clientHeight;
        this.viewer = new ROS3D.Viewer({
            divID : divID,
            width : width,
            height : height,
            background : '#FFF8DC',
            intensity : 0.8,
            cameraPose: this.cameraPose,
            alpha : 0,
            displayPanAndZoomFrame : true,
        });
        this.viewer.addObject(new ROS3D.Grid());
        // Setup a client to listen to TFs.
        this.tfClient = new ROSLIB.TFClient({
            ros : this.ros,
            fixedFrame : 'map',//'/base_footprint',
            angularThres : 0.01,
            transThres : 0.01,
            rate : 30.0,
        });
        var stlClient = new ROS3D.UrdfClient({
            ros : this.ros,
            tfClient : this.tfClient,
            path : './static',
            rootObject : this.viewer.scene,
        });
        // // 监听模型加载完成事件
        // stlClient.on('change', function () {
        //     console.log('测试')
        // })
        
        this.updata()

    }
    updata() {
        $(".rei_viewer_loading").fadeOut("slow")
        var GridClient = new ROS3D.OccupancyGridClient({
            ros : this.ros,
            rootObject : this.viewer.scene,
            topic : '/map',
            tfClient : this.tfClient,
            offsetPose : p,
            color : {r:255,g:255,b:255},
            opacity : 0.5,
            continuous: true
        })

        console.log(GridClient)

        new ROS3D.Pose({
            ros : this.ros,
            tfClient : this.tfClient,
            rootObject : this.viewer.scene,
            topic : 'now_pose',
            color : 0x00EE00,
        });
        new ROS3D.Pose({
            ros : this.ros,
            tfClient : this.tfClient,
            rootObject : this.viewer.scene,
            topic : 'move_base_simple/goal',
            color : 0xFF0000,
        })
        new ROS3D.Path({
            ros : this.ros,
            tfClient : this.tfClient,
            rootObject : this.viewer.scene,
            topic : '/move_base/TebLocalPlannerROS/local_plan',
            color : 0x7CFC00
        })
        var v1 = new ROSLIB.Vector3({
            x : 0,
            y : 0,
            z : -0.05
        });
    
        var q1 = new ROSLIB.Quaternion({
            x : 0,
            y : 0,
            z : 0,
            w : 1
        });
        var p = new ROSLIB.Pose({
            position : v1,
            orientation : q1
        });
        new ROS3D.LaserScan({
            ros : this.ros,
            tfClient : this.tfClient,
            rootObject : this.viewer.scene,
            topic : this.scan_topic,
            material: {size: 0.1,color: 0x00BFFF},
        })
    }

    stop() {
        $(".rei_viewer_loading").fadeIn("slow")
        this.viewer.stop()
        this.viewer.renderer.dispose();
        var canvas = this.viewer.renderer.domElement;
        canvas.parentNode.removeChild(canvas);
    }
    resize() {
        var width = this.div.clientWidth;
        var height = this.div.clientHeight;
        this.viewer.resize(width, height)
    }
}

rei_ros.add_joyToHtml = function (options) {
    options = options || {};
    var id  = options.divID;
    var axis = options.axis || 'left';
    var ros = options.ros;
    var topic = options.topic || '/cmd_vel';
    var throttle = options.throttle || 1.0;
    var min_linear_speed = options.min_linear_speed || 0.1;
    var max_linear_speed = options.max_linear_speed || 0.5;
    var min_angle_speed = options.min_angle_speed || 0.3;
    var max_angle_speed = options.max_angle_speed || 1.5;

    var cmdVel = new ROSLIB.Topic({
        ros : ros,
        name : topic,
        messageType : 'geometry_msgs/Twist'
    });
    const NewDiv = document.getElementById(id)
    const joyDiv = NewDiv.querySelector('.rei-ros-joy')
    const axisDiv = joyDiv.querySelector('.rei-ros-joy-axis')
    var isDragging = false;
    var offsetX, offsetY;    

    function pubVel(x,y,z) {
        if(ros_is_linked == true){
            x = x*max_linear_speed*throttle;
            y = y*max_linear_speed*throttle;
            z = z*max_angle_speed*throttle;
            if(Math.abs(x)<min_linear_speed){x=0};
            if(Math.abs(y)<min_linear_speed){y=0};
            if(Math.abs(z)<min_angle_speed){z=0};
            var twist = new ROSLIB.Message({
                angular : {
                    x : 0,
                    y : 0,
                    z : z,
                },
                linear : {
                    x : x,
                    y : y,
                    z : 0
                }
            });
            cmdVel.publish(twist);
        }else{
            console.log("ros 未连接");
        };
    }

    function startDrag(event) {
        pubVel(0,0,0)
        isDragging = true;
        offsetX = event.clientX - axisDiv.offsetLeft;
        offsetY = event.clientY - axisDiv.offsetTop;
        event.preventDefault();
        axisDiv.style.cursor = 'grabbing';
    }
    
    function drag(event) {
        if (isDragging) {
            const x = event.clientX - offsetX;
            const y = event.clientY - offsetY;
            var X = 40-x;
            var Y = 40-y;
            const R = Math.sqrt((X * X)+(Y * Y));
            const rate = 60/R;
            var dst_X;
            var dst_Y;
            if (rate > 6){
                axisDiv.style.left = x + 'px';
                axisDiv.style.top = y + 'px';
                pubVel(0,0,0)
            }else if(rate > 1){
                axisDiv.style.left = x + 'px';
                axisDiv.style.top = y + 'px';
                dst_X = X/60;
                dst_Y = Y/60;
                if(axis == 'left'){
                    pubVel(dst_Y,dst_X,0)
                }else if (axis == 'reight'){
                    pubVel(dst_Y,0,dst_X)
                }
            }else if(rate > 0.5){
                dst_X = X*rate/60
                dst_Y = Y*rate/60
                axisDiv.style.left = (40-X*rate) + 'px';
                axisDiv.style.top = (40-Y*rate) + 'px';
                if(axis == 'left'){
                    pubVel(dst_Y,dst_X,0)
                }else if (axis == 'reight'){
                    pubVel(dst_Y,0,dst_X)
                }
            }else{
                isDragging = false;
                axisDiv.style.cursor = 'grab';
                axisDiv.style.left = 40 + 'px';
                axisDiv.style.top = 40 + 'px';
                pubVel(0,0,0)
            };
        }
    }
    
    function stopDrag() {
        isDragging = false;
        axisDiv.style.cursor = 'grab';
        axisDiv.style.left = '40px';
        axisDiv.style.top = '40px';
        pubVel(0,0,0)
    }

    axisDiv.addEventListener('mousedown', startDrag);
    document.addEventListener('mousemove', drag);
    document.addEventListener('mouseup', stopDrag);
}

rei_ros.add_keyboardToHtml = function (options){
    var that = this;
    options = options || {};
    var ros = options.ros;
    var topic = options.topic || '/cmd_vel';
    var throttle = options.throttle || 0.5;
    var is_open = options.is_open || false
    var x = 0;
    var y = 0;
    var z = 0;

    this.scale = 1

    var cmdVel = new ROSLIB.Topic({
        ros : ros,
        name : topic,
        messageType : 'geometry_msgs/Twist'
    });


    var start = false
    let startTime = Date.now();
    var sp_start = false
    
    function activation(time) {    
        return 2/(1+Math.pow(Math.E,-throttle*time)) - 1;
    };
    function pub_cmdvel(x,y,z) {
        // var speed = 0
        // console.log("这个是===="+Math.abs(x)+Math.abs(y)+Math.abs(z))
        // if (Math.abs(x)+Math.abs(y)+Math.abs(z)==0){
        //     start = true
        //     sp_start = false
        //     console.log(x,y,z)
        // }else{
        //     if(start==true){
        //         startTime = Date.now();
        //         start=false
        //         sp_start = true
        //     }
        //     if(sp_start==true){
        //         let endTime = Date.now();
        //         var time = Math.floor((endTime - startTime) / 1000)
        //         speed = activation(time)
        //     }
        // }
        
        var twist = new ROSLIB.Message({
            angular : {
                x : 0,
                y : 0,
                z : z
            },
            linear : {
                x : x,
                y : y,
                z : 0
            }
        });
        cmdVel.publish(twist);
    }

    this.handleKey = function(keyCode,keyDown) {        
        var pub = true;
        var speed = 0;
        if(keyDown === true) {
            speed = this.scale * 0.5// * this.activation(time)
        }
        switch (keyCode) {
            case 65:
            case 37:
                // turn left
                z = 1 * speed;
                break;
            case 87:
            case 38:
                // up
                x = 0.5 * speed;
                break;
            case 68:
            case 39:
                // turn right
                z = -1 * speed;
                break;
            case 83:
            case 40:
                // down
                x = -0.5 * speed;
                break;
            case 69:
                // strafe rights
                y = -0.5 * speed;
                break;
            case 81:
                // strafe left
                y = 0.5 * speed;
                break;
            default:
                pub = false;
        }
        // publish the command
        if (pub === true) {
            pub_cmdvel(x,y,z)
        }
    };
    function debounce(delay) {
        let timer;
        return function (e) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                that.handleKey(e.keyCode, false,0);
            }, delay);
        };
    }
    var body = document.getElementsByTagName('body')[0];
    body.addEventListener('keydown', function(e) {
        if(is_open == true){
            that.handleKey(e.keyCode, true);
        }else{
            that.handleKey(e.keyCode, false);
        }
    }, false);
    body.addEventListener('keyup', function(e) {
        console.log('键盘抬起')
        that.handleKey(e.keyCode, false,0);
    }, false);
    function handleKeyUp() {
        that.handleKey(e.keyCode, false);
    }
    body.addEventListener('keyup',debounce(200))
    // body.addEventListener('mouseleave', function () {
    //     console.log('鼠标已离开浏览器窗口');
    //     // 在这里可以添加其他你想要执行的操作，比如暂停某些动画、停止定时器等

    // });
    // 监听窗口失去焦点事件
    window.addEventListener('blur', function () {
        if(is_open==true){
            pub_cmdvel(0,0,0)
            is_open = false
        }
        console.log('你已经离开了当前页面。');
    });
    // 监听窗口获得焦点事件
    window.addEventListener('focus', function () {
        if(is_open==false){
            is_open = true
        }
        console.log('你已经回到了当前页面。');
    });
}

class NavPose{
    constructor(options){
        options = options || {}
        this.ros = options.ros
        this.goalPublisher = new ROSLIB.Topic({
            ros: ros,
            name: '/move_base_simple/goal',
            messageType: 'geometry_msgs/PoseStamped'
        });
    }

    get_NavPose () {
        var pose = {}
        var wait = true
        var tfClient = new ROSLIB.TFClient({
            ros: this.ros,
            fixedFrame: 'map', // 设置固定参考系，通常为'world'，也需根据实际情况改变
            angularThres: 0.01, // 角度阈值，用于决定何时更新TF信息
            transThres: 0.01 // 平移阈值，用于决定何时更新TF信息
        });
        tfClient.subscribe('base_footprint', function (data) {
            pose = {
                position: {
                    x: data.translation.x,
                    y: data.translation.y,
                    z: data.translation.z
                },
                orientation: {
                    x: data.rotation.x,
                    y: data.rotation.y,
                    z: data.rotation.z,
                    w: data.rotation.w
                }
            }
            console.log(pose)
            wait = false
        })
        console.log('等待结束')
        return pose
    }

    set_NavPose () {
        // var pose = this.PoseList[0]
        var goalMessage = new ROSLIB.Message({
            header:{
                seq : 0,
                stamp : {
                    secs : 0,
                    nsecs : 0
                },
                frame_id:"map",
            },
            pose: {
                position: {
                    x: pose.translation.x,
                    y: pose.translation.y,
                    z: pose.translation.z
                },
                orientation: {
                    x: pose.rotation.x,
                    y: pose.rotation.y,
                    z: pose.rotation.z,
                    w: pose.rotation.w
                }
            }
        });
        this.goalPublisher.publish(goalMessage)
    }
}

var robot_api = (function (exports,ROBOTID) {
    class Pose{
        constructor(options){
            options = options || {};
            this.frame_id = options.frame_id || 'map';
            this.position = options.poseition || {x:0,y:0,z:0};
            this.orientation = options.orientation || {x:0,y:0,z:0,w:0};
        }
        get_poseition(){
            return this.poseition
        }
        get_orientation(){
            return this.orientation
        }
        get_RPY(){
            var x = this.orientation.x;
            var y = this.orientation.y;
            var z = this.orientation.z;
            var w = this.orientation.w;
            var pitch = Math.atan2(2 * (w * y + x * z), 1 - 2 * (y * y + z * z));
            var yaw = Math.atan2(2 * (w * z + x * y), 1 - 2 * (x * x + z * z));
            var roll = Math.asin(2 * (w * x - y * z));
            return {roll:roll,pitch:pitch,yaw:yaw};
        }
    }

    class RobotBase{
        constructor(options){
            options = options || {};
            // this.ros = options.ros;
            this.ros = ros
            this.moveBaseClient = new ROSLIB.ActionClient({
                ros:this.ros,
                serverName:'move_base',
                actionName:'move_base_msgs/MoveBaseAction'
            });
            this.moveBaseClient.cancel()
        }

        goto_NavGoal(pose){
            var pose = pose || new Pose;
            return new Promise(resolve => {
                var goal = new ROSLIB.Goal({
                    actionClient: this.moveBaseClient,
                    goalMessage: {
                        target_pose:{
                            header:{
                                stamp: {
                                    secs: 0,
                                    nsecs: 0
                                },
                                frame_id: 'map',
                            },
                            pose: {
                                position: {
                                    x: pose.position.x,
                                    y: pose.position.y,
                                    z: pose.position.z
                                },
                                orientation: {
                                    x: pose.orientation.x,
                                    y: pose.orientation.y,
                                    z: pose.orientation.z,
                                    w: pose.orientation.w
                                }
                            }
                        }
                    }
                })
                var positionTopic = new ROSLIB.Topic({
                    ros: ros,
                    name: 'move_base/result',
                    messageType: 'move_base_msgs/MoveBaseAction'
                });
                positionTopic.subscribe(function (message) {
                    resolve(message);
                    rei_log('机器人到达目标位置，消息:', message);
                });
                console.log('机器人开始导航');
                goal.send();
                rei_log('发送导航命令');
            })
        }

        relative_move(x,y,theta){
            x = x/100;
            y = y/100;
            theta = theta/180*Math.PI;
            return new Promise(resolve => {
                console.log('开始相对移动')
                var RelativeMoveClient = new ROSLIB.Service({
                    ros: this.ros,
                    name: 'relative_move',
                    serverType: 'relative_move/SetRelativeMove'
                })
                var request = new ROSLIB.ServiceRequest({
                    goal:{
                        x: x,
                        y: y, 
                        theta: theta
                    },
                    global_frame: 'map',
                    avoid_obstacle: false,
                    finishStopObstacle: false,
                    is_omni: 1,
                })
                RelativeMoveClient.callService(request, function(result) {
                    console.log('"relative_move result": ', result.success);
                    resolve(result);
                });
                console.log('相对移动结束')
            })
        }

        goto_track(ar_id){
            return new Promise(resolve => {
                console.log('对准二维码中')
                var TrackClient = new ROSLIB.Service({
                    ros: this.ros,
                    name: 'track',
                    serverType:'ar_pose/Track',
                })
                var request = new ROSLIB.ServiceRequest({
                    ar_id: ar_id,
                    goal_dist: 0.4
                })
                TrackClient.callService(request, function(result) {
                    console.log('"relative_move result": ', result.success);
                    resolve(result.success);
                });
                console.log('对准对准结束')
            })
        }
    }

    class ReiError extends Error {
        constructor(message) {
            super(message);
            this.name = 'ReiError'
        }
    }
    exports.RobotBase = RobotBase;
    exports.Pose = Pose;
    return exports
})({},'oryxbot');
